﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Security;

namespace Camalot.Common.Membership {
	// http://codefirstmembership.codeplex.com
	// Microsoft Public License (Ms-PL)
	// 
	// This license governs use of the accompanying software. If you use the software, you accept this license. 
	// If you do not accept the license, do not use the software.
	// 
	// 1. Definitions
	//
	// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as 
	// under U.S. copyright law.
	// 
	// A "contribution" is the original software, or any additions or changes to the software.
	// 
	// A "contributor" is any person that distributes its contribution under this license.
	// 
	// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
	// 
	// 2. Grant of Rights
	// 
	// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations 
	// in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to 
	// reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution 
	// or any derivative works that you create.
	// 
	// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in 
	// section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed 
	// patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution 
	// in the software or derivative works of the contribution in the software.
	// 
	// 3. Conditions and Limitations
	// 
	// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or 
	// trademarks.
	// 
	// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the 
	// software, your patent license from such contributor to the software ends automatically.
	// 
	// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and 
	// attribution notices that are present in the software.
	// 
	// (D) If you distribute any portion of the software in source code form, you may do so only under this license 
	// by including a complete copy of this license with your distribution. If you distribute any portion of the 
	// software in compiled or object code form, you may only do so under a license that complies with this license.
	// 
	// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express 
	// warranties, guarantees or conditions. You may have additional consumer rights under your local laws which 
	// this license cannot change. To the extent permitted under your local laws, the contributors exclude the 	
	// implied warranties of merchantability, fitness for a particular purpose and non-infringement.
	public class CodeFirstRoleProvider : RoleProvider {
		public override string ApplicationName {
			get {
				return this.GetType ( ).Assembly.GetName ( ).Name.ToString ( );
			}
			set {
				this.ApplicationName = this.GetType ( ).Assembly.GetName ( ).Name.ToString ( );
			}
		}

		public override bool RoleExists ( string roleName ) {
			if ( string.IsNullOrEmpty ( roleName ) ) {
				return false;
			}
			using ( var Context = new DataContext ( ) ) {
				Role Role = null;
				Role = Context.Roles.FirstOrDefault ( Rl => Rl.RoleName == roleName );
				if ( Role != null ) {
					return true;
				} else {
					return false;
				}
			}
		}

		public override bool IsUserInRole ( string username, string roleName ) {
			if ( string.IsNullOrEmpty ( username ) ) {
				return false;
			}
			if ( string.IsNullOrEmpty ( roleName ) ) {
				return false;
			}
			using ( var Context = new DataContext ( ) ) {
				User User = null;
				User = Context.Users.FirstOrDefault ( Usr => Usr.Username == username );
				if ( User == null ) {
					return false;
				}
				Role Role = Context.Roles.FirstOrDefault ( Rl => Rl.RoleName == roleName );
				if ( Role == null ) {
					return false;
				}
				return User.Roles.Contains ( Role );
			}
		}

		public override string[] GetAllRoles ( ) {
			using ( var Context = new DataContext ( ) ) {
				return Context.Roles.Select ( Rl => Rl.RoleName ).ToArray ( );
			}
		}

		public override string[] GetUsersInRole ( string roleName ) {
			if ( string.IsNullOrEmpty ( roleName ) ) {
				return null;
			}
			using ( var Context = new DataContext ( ) ) {
				Role Role = null;
				Role = Context.Roles.FirstOrDefault ( Rl => Rl.RoleName == roleName );
				if ( Role != null ) {
					return Role.Users.Select ( Usr => Usr.Username ).ToArray ( );
				} else {
					return null;
				}
			}
		}

		public override string[] GetRolesForUser ( string username ) {
			if ( string.IsNullOrEmpty ( username ) ) {
				return null;
			}
			using ( var Context = new DataContext ( ) ) {
				User User = null;
				User = Context.Users.FirstOrDefault ( Usr => Usr.Username == username );
				if ( User != null ) {
					return User.Roles.Select ( Rl => Rl.RoleName ).ToArray ( );
				} else {
					return null;
				}
			}
		}

		public override string[] FindUsersInRole ( string roleName, string usernameToMatch ) {
			if ( string.IsNullOrEmpty ( roleName ) ) {
				return null;
			}

			if ( string.IsNullOrEmpty ( usernameToMatch ) ) {
				return null;
			}

			using ( var Context = new DataContext ( ) ) {

				return ( from Rl in Context.Roles from Usr in Rl.Users where Rl.RoleName == roleName && Usr.Username.Contains ( usernameToMatch ) select Usr.Username ).ToArray ( );
			}
		}

		public override void CreateRole ( string roleName ) {
			if ( !string.IsNullOrEmpty ( roleName ) ) {
				using ( var Context = new DataContext ( ) ) {
					Role Role = null;
					Role = Context.Roles.FirstOrDefault ( Rl => Rl.RoleName == roleName );
					if ( Role == null ) {
						Role NewRole = new Role {
							RoleId = Guid.NewGuid ( ),
							RoleName = roleName
						};
						Context.Roles.Add ( NewRole );
						Context.SaveChanges ( );
					}
				}
			}
		}

		public override bool DeleteRole ( string roleName, bool throwOnPopulatedRole ) {
			if ( string.IsNullOrEmpty ( roleName ) ) {
				return false;
			}
			using ( var Context = new DataContext ( ) ) {
				Role Role = null;
				Role = Context.Roles.FirstOrDefault ( Rl => Rl.RoleName == roleName );
				if ( Role == null ) {
					return false;
				}
				if ( throwOnPopulatedRole ) {
					if ( Role.Users.Any ( ) ) {
						return false;
					}
				} else {
					Role.Users.Clear ( );
				}
				Context.Roles.Remove ( Role );
				Context.SaveChanges ( );
				return true;
			}
		}

		public override void AddUsersToRoles ( string[] usernames, string[] roleNames ) {
			using ( var Context = new DataContext ( ) ) {
				List<User> Users = Context.Users.Where ( Usr => usernames.Contains ( Usr.Username ) ).ToList ( );
				List<Role> Roles = Context.Roles.Where ( Rl => roleNames.Contains ( Rl.RoleName ) ).ToList ( );
				foreach ( User user in Users ) {
					foreach ( Role role in Roles ) {
						if ( !user.Roles.Contains ( role ) ) {
							user.Roles.Add ( role );
						}
					}
				}
				Context.SaveChanges ( );
			}
		}

		public override void RemoveUsersFromRoles ( string[] usernames, string[] roleNames ) {
			using ( var Context = new DataContext ( ) ) {
				foreach ( String username in usernames ) {
					String us = username;
					User user = Context.Users.FirstOrDefault ( U => U.Username == us );
					if ( user != null ) {
						foreach ( String roleName in roleNames ) {
							String rl = roleName;
							Role role = user.Roles.FirstOrDefault ( R => R.RoleName == rl );
							if ( role != null ) {
								user.Roles.Remove ( role );
							}
						}
					}
				}
				Context.SaveChanges ( );
			}
		}
	}
}
